import React from "react";
import styles from './index.module.scss';
import {
  ArrowsAltOutlined,
  CodepenOutlined,
  ShrinkOutlined,
  QuestionCircleOutlined,
  FormatPainterOutlined,
  CloseCircleOutlined,
  SmileOutlined,
  ClockCircleTwoTone,
  DownOutlined
} from '@ant-design/icons';
import {isFunction} from 'lodash';


// ACE 依赖
import AceEditor from "react-ace";
import "ace-builds/webpack-resolver";// 必须引入该依赖，解决webWorker问题
import 'ace-builds/src-noconflict/ext-language_tools'; // 语法自动提示
// import 'ace-builds/src-noconflict/worker-javascript'; // 代码检查工具
import {beautify} from 'ace-builds/src-noconflict/ext-beautify'; // 代码格式化
import "ace-builds/src-noconflict/mode-scss";
import "ace-builds/src-noconflict/snippets/scss"; // js语法片段
import 'ace-builds/src-noconflict/theme-github'; // 主题
import 'ace-builds/src-noconflict/ext-searchbox'; // 语法自动提示
// TS
import {Ace} from "ace-builds";
import Completion = Ace.Completion;
import Editor = Ace.Editor;

import {Button, Tooltip, Dropdown, Menu} from "antd";
import {SaveOutlined} from "@ant-design/icons/lib";
import dvTool from "@/pages/dv/dvTool";

import * as classnames from "classnames";


interface IProps {
  onChangeMode?(mode: 'drawer' | 'modal'): void;
  mode?: 'drawer' | 'modal';
}

interface IState {
  size: {
    width: number;
    height: number;
  },
  code: string;
  syntax: 'CSS' | 'SCSS';
  saveStatus: boolean;
  changeStatus: boolean;
  errorMsg: string[];
}

export default class CSSEditorBox extends React.PureComponent<IProps, IState> {

  public editor: Editor;
  public errorAnnotations: any[]; // 语法错误集合

  constructor(props: IProps) {
    super(props);
    this.state = {
      size: {
        width: 500,
        height: 400
      },
      code: dvTool.cssConfig.code || '',
      errorMsg: [],
      saveStatus: false,
      changeStatus: false,
      syntax: dvTool.cssConfig.syntax || 'CSS', // 语法
    }
    this.errorAnnotations = [];
  }

  componentDidMount(): void {

  }

  onChange = (newValue: string) => {
    dvTool.isEditingOfCss = true;
    this.setState({
      code: newValue,
      saveStatus: false,
      changeStatus: true
    }, () => {
      // css自动实时保存
      // scss主动式：需要请求后端接口得到css编译结果
      if (this.state.syntax === 'CSS') {
        this.saveCode(newValue)
      }

    });

  }

  /**
   * 保存代码
   * @param code
   */
  saveCode = (code: string) => {
    this.applyCode(code, (cssCode: string) => {
      // 没问题就保存

      dvTool.isEditingOfCss = false;
      dvTool.updateCssConfig({
        syntax: this.state.syntax,
        code: cssCode
      })
      this.setState({
        saveStatus: true,
        changeStatus: false
      })
    });
  }

  /**
   * 应用代码
   * @param code
   * @param onOk
   */
  applyCode = (code: string, onOk?: (cssCode: string) => void) => {
    // MD 1.检查代码
    this.checkCss(code).then((cssCode: string) => {
      // MD 2.执行代码
      this.runCode(cssCode, () => {
        this.setState({
          errorMsg: [],
        });
        this.errorAnnotations = [];
        onOk && onOk(cssCode);
      });
    }, resp => {
      const {row, column} = resp[0];
      this.setState({
        errorMsg: ['编辑器内容语法有误！', `row: ${row}, column: ${column}`]
      });
      this.errorAnnotations = resp;
    });

  }

  /**
   * 检查css语法
   * 如果是scss语法需要转化
   * @param code
   */
  checkCss(code: string) {
    return new Promise(((resolve, reject) => {
      const annotations = this.editor.getSession().getAnnotations();
      const errorAnnotations = annotations.filter(item => item.type === 'error');
      if (errorAnnotations.length > 0) {
        reject(errorAnnotations);
      } else {
        let cssCode = code;
        // 语法编译
        if (this.state.syntax === 'SCSS') {

        }
        resolve(cssCode);
      }
    }))
  }

  /**
   * 执行代码
   * @param cssCode
   * @param onOk
   */
  runCode(cssCode: string, onOk: () => void) {
    dvTool.reloadCss(cssCode, () => {
      onOk && onOk();
    }, (e) => {
      this.setState({
        errorMsg: ['[编程运行错误]: ' + e.message, '']
      })
    });
  }

  /**
   * 格式化代码
   */
  formatCode = () => {
    beautify(this.editor.getSession());
  }

  /**
   * 定位到错误行数
   */
  findErrorRow = () => {
    const {row, column} = this.errorAnnotations[0];
    this.editor.scrollToRow(row);
    this.editor.gotoLine(row + 1, column, true);
  }

  /**
   * 编辑器加载完成时
   * @param editor
   */
  async complete(editor: any) {
    this.editor = editor;
    // MD 1、动态加载组件框架对应的语法提示
    let cssCompletion: Completion[] = [];

    // MD 2、为编辑器注入自动提示语句
    editor.completers.push({
      getCompletions: function (editor, session, pos, prefix, callback) {
        callback(null, cssCompletion);
      }
    });
  }

  onChangeMode(mode: 'modal' | 'drawer') {
    isFunction(this.props.onChangeMode) && this.props.onChangeMode(mode);
  }

  handleChangeCss = ({key}: any) => {
    if (key !== this.state.syntax) {
      this.setState({
        syntax: key
      })
    }
  }

  getCssMenu() {
    return <Menu
      onClick={this.handleChangeCss}
    >
      <Menu.Item key="CSS">
        CSS
      </Menu.Item>
      <Menu.Item key="SCSS" disabled>
        SCSS
      </Menu.Item>
    </Menu>
  }


  render(): React.ReactNode {

    const aceProps = {
      placeholder: this.state.syntax === 'CSS' ? 'CSS语法请在组件className命名空间内书写\n' +
        '请勿造成全局污染！' : '已自动生成scss命名空间',
      width: '100%',
      height: 'calc(100% - 24px)',
      mode: "scss",
      theme: "github",
      onChange: this.onChange,
      name: "UNIQUE_ID_OF_DIV",
      editorProps: {
        $blockScrolling: true
      },
      fontSize: 12,
      value: this.state.code,
      showPrintMargin: true,
      showGutter: true,
      highlightActiveLine: true,
      setOptions: {
        enableBasicAutocompletion: true,
        enableLiveAutocompletion: true,
        enableSnippets: true,
        showLineNumbers: true,
        tabSize: 2,
      },
      onLoad: this.complete.bind(this),
      commands: [{    //命令是键绑定数组。
        name: 'saveFile', //键绑定的名称。
        bindKey: {win: 'Ctrl-S', mac: 'Command-S'}, //用于命令的组合键。
        exec: () => {
          this.saveCode(this.state.code);
        }   //重新绑定命令的名称
      }]
    }

    return <div className={styles.cssEditorBox}>
      <ul className={styles.tool}>
        {/*左侧互动按钮*/}
        <li>
          {
            this.props.mode === 'drawer' && <Tooltip title={'转换成悬浮模态框展示'}>
              <ArrowsAltOutlined onClick={() => this.onChangeMode('modal')}/>
            </Tooltip>
          }

          {
            this.props.mode === 'modal' && <Tooltip title={'转换成抽屉盒子展示'}>
              <ShrinkOutlined onClick={() => this.onChangeMode('drawer')}/>
            </Tooltip>
          }
        </li>

        <li><Tooltip title={'切换CSS语法'}>
          <Dropdown.Button
            trigger={['click']}
            className={styles.cssSelectBox}
            overlay={this.getCssMenu()}
            placement="bottomCenter"
            icon={<DownOutlined/>}
            size={'small'}
          >
            {this.state.syntax}
          </Dropdown.Button>
        </Tooltip></li>

        <li><Tooltip title={'格式化代码'}><FormatPainterOutlined onClick={this.formatCode}/></Tooltip></li>
        {this.state.changeStatus && <li><Tooltip title={'代码已变更，请注意保存'}><ClockCircleTwoTone/></Tooltip></li>}

        <li><Tooltip title={'帮助文档'}><QuestionCircleOutlined/></Tooltip></li>

        {/*右侧互动按钮*/}
        <li className={styles.apply}>
          <Tooltip title={'只运行不保存'}>
            <Button size="small" type={'primary'} onClick={() => this.applyCode(this.state.code)}>运行</Button>
          </Tooltip>
        </li>
        <li className={styles.save}>
          <Tooltip title={'保存并运行'}>
            <Button
              size="small"
              type={'primary'}
              icon={<SaveOutlined/>}
              onClick={() => this.saveCode(this.state.code)}
            />
          </Tooltip>
        </li>
      </ul>
      {
        !1 &&
          <div>
            <div className={styles.codingSign}>
              <CodepenOutlined/>
              <div>请在悬浮模态框中编程</div>
            </div>
          </div>
      }
      <AceEditor
        {...aceProps}
        height={'calc(100% - 50px)'}
      />

      <AceInfoTip
        errorMsg={this.state.errorMsg}
        saveStatus={this.state.saveStatus}
        findErrorRow={this.findErrorRow}
      />
    </div>
  }
}

/**
 * 错误提示框
 * @param errorMsg
 * @param findErrorRow
 * @constructor
 */
interface IAceInfoTip {
  errorMsg: string[];
  saveStatus: boolean;

  findErrorRow(): void;
}

function AceInfoTip({errorMsg = [], saveStatus = false, findErrorRow}: IAceInfoTip) {
  return <div className={classnames(styles.errorTip, {
    [styles.hidden]: !errorMsg[0] && !saveStatus,
    [styles.success]: saveStatus
  })}>
    {
      !!errorMsg[0] && <React.Fragment>
        <span className={styles.errorMsg}><CloseCircleOutlined/> {errorMsg[0]}</span>
        <Tooltip title={'定位到语法错误位置'}>
          <span className={styles.row} onClick={findErrorRow}>{errorMsg[1]}</span>
        </Tooltip>
      </React.Fragment>
    }
    {
      saveStatus && <span className={styles.successMsg}><SmileOutlined/> 已保存 </span>
    }

  </div>
}
